/**
 * @file serial.c
 * @brief Serial Interface
 *
 * @version Fixed
 * @author yemit
 * @date 2010-06-17
 */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <key.h>
#include "config.h"
#include "device.h"
#include "pm.h"
#include "apitrans.h"


#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

typedef struct {
        int ser_fd;
        char * devs;
}serial_t;

static serial_t sers[] = CONFIG_SERIAL_DEVS;
static int dev_nr = ARRAY_SIZE(sers);
extern int key_fd;

void serial_dev_init(void)
{
        int i;
        for (i = 0; i < dev_nr; i ++) {
                sers[i].ser_fd = open(sers[i].devs, O_RDWR|O_NONBLOCK);
        }
}

#ifdef CONFIG_USB_CDC
int usb_onoff(uint32_t onoff)
{
    if(onoff)
	    return ioctl(sers[4].ser_fd, IOCTL_USB_CONNECT, NULL);
    else
        return ioctl(sers[4].ser_fd, IOCTL_USB_DISCONNECT, NULL);

    return 0;
}
#endif

int serial_init(uint32_t id, serial_info_t * info)
{
#ifdef CONFIG_SERIAL_SWITCH
	int index;
	if(id >= SERIAL_VIR_MIN && id < SERIAL_VIR_MAX) {
		index = id;
		id =  CONFIG_SERIAL_MUX_ID;
		if (ioctl(sers[id].ser_fd, IOCTL_SERIAL_SWITCH, (void*)index) < 0)
			return -1;
	}
#	ifdef CONFIG_USB_CDC
		if (id == SERIAL_VIR_MAX) {
			id = 4;
		}
#	endif
#endif
	if (id >= dev_nr) {
		return -1;
	}

	if (info) {
		if (ioctl(sers[id].ser_fd,IOCTL_SERIAL_SET_PARAM, info) < 0)
		return -1;
	}
return 0;
}


int serial_write(uint32_t id, uint8_t * buffer, size_t len)
{
#ifdef CONFIG_SERIAL_SWITCH
	if(id >= SERIAL_VIR_MIN && id < SERIAL_VIR_MAX) {
		id =  CONFIG_SERIAL_MUX_ID;
	}

#	ifdef CONFIG_USB_CDC
	if (id == SERIAL_VIR_MAX) {
		id = 4;
	}
#	endif
#endif
	if (id >= dev_nr) {
        return -1;
    }

    if (sers[id].ser_fd < 0) {
        return -1;
    }
    
    if (buffer && len) {
            fd_set writefds;
            struct timeval tvp;
            int maxfd, sendcount, ret = 0 ;

            maxfd = key_fd > sers[id].ser_fd ? key_fd : sers[id].ser_fd;
            maxfd ++;
            sendcount = 0 ;
            tvp.tv_sec = 10 ;
            tvp.tv_usec = 0 ;

            while (sendcount < len) {
                    FD_ZERO(&writefds) ;
                    FD_SET(key_fd, &writefds) ;
                    FD_SET(sers[id].ser_fd, &writefds) ;
                    ret = select(maxfd, NULL, &writefds, NULL, &tvp) ;
                    if (ret <= 0)
                        break;
                    if (FD_ISSET(sers[id].ser_fd, &writefds)) {
                            ret = write(sers[id].ser_fd, buffer + sendcount, len - sendcount) ;
                            if (ret <= 0)
                                return ret ;
                            sendcount += ret ;
                    }
                    else {
                            if (read(key_fd, &ret, 1) != 1) continue;
                            if (ret == KEY_ESC) return -2;
                    }
            }
            return sendcount ;
    }
    return 0;
}

int serial_read(uint32_t id, uint8_t * buffer, size_t len, int timeout)
{
    int ret = 0;
    
#ifdef CONFIG_SERIAL_SWITCH
	if(id >= SERIAL_VIR_MIN && id < SERIAL_VIR_MAX) {
		id =  CONFIG_SERIAL_MUX_ID;
	}
#	ifdef CONFIG_USB_CDC
	if (id == SERIAL_VIR_MAX) {
		id = 4;
	}
#	endif
#endif
	if (id >= dev_nr) {
        return -1;
    }
    
    if (sers[id].ser_fd < 0) {
        return -1;
    }
    if (buffer && len) {
            fd_set readfds;
            struct timeval tvp, *vp;
            int maxfd, readcount;

            maxfd = key_fd > sers[id].ser_fd ? key_fd : sers[id].ser_fd;
            maxfd ++;
            readcount = 0 ;
            if (timeout == 0) {
                    vp = NULL;
            }
            else {
                    tvp.tv_sec = timeout / 1000;
                    tvp.tv_usec = (timeout % 1000) * 1000 ;
                    vp = &tvp;
            }
            while (readcount < len) {
                    FD_ZERO(&readfds) ;
                    FD_SET(key_fd, &readfds) ;
                    FD_SET(sers[id].ser_fd, &readfds) ;
                    ret = select(maxfd, &readfds, NULL, NULL, vp) ;
                    if (ret <= 0) {
                            break;
                    }
                    if (FD_ISSET(sers[id].ser_fd, &readfds)) {
                            ret = read(sers[id].ser_fd, buffer + readcount, len - readcount);
                            if (ret <= 0) {
                                    break ;
                            }
                            readcount += ret ;
                    }
                    if (FD_ISSET(key_fd, &readfds)) {
                            if (read(key_fd, &ret, 1) != 1) continue;
                            if (ret == KEY_ESC) return -2;
                    }
            }
            return readcount;
    }
    return 0;
}

int serial_clrbuf(uint32_t id, int rx, int tx)
{
#ifdef CONFIG_SERIAL_SWITCH
	if(id >= SERIAL_VIR_MIN && id < SERIAL_VIR_MAX) {
		id =  CONFIG_SERIAL_MUX_ID;
	}
#	ifdef CONFIG_USB_CDC
		if (id == SERIAL_VIR_MAX) {
			id = 4;
		}
#	endif
#endif
	if (id >= dev_nr) {
		return -1;
	}
    
    if (sers[id].ser_fd < 0) {
        return -1;
    }    
    
    if (rx)
            ioctl(sers[id].ser_fd,IOCTL_SERIAL_CLR_RXBUF, NULL);
    if (tx)
            ioctl(sers[id].ser_fd,IOCTL_SERIAL_CLR_TXBUF, NULL);
    return 0;
}
APITRANS_API_CALL(SERIAL_CLRBUF_API, serial_clrbuf);
APITRANS_API_CALL(SERIAL_INIT_API, serial_init);
APITRANS_API_CALL(SERIAL_READ_API, serial_read);
APITRANS_API_CALL(SERIAL_WRITE_API, serial_write);



